package com.example.springboot.ytest.ytest;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.HashMap;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * 修改密码工具类，不提供接口
 * @author: yeyi
 * @date: 2019-05-10
 **/
@Service
public class PasswordUtil {

    Logger logger = LoggerFactory.getLogger(this.getClass());
    
    /**
     * 设置密码验证码输错次数
     * @author yeyi
     * @date 2019/5/10 15:29
     **/
    public static final String PWD_SMS_ERR_COUNT_KEY = "gmall:gmall-customer-sso:SMSService:PWD_SMS_ERR_COUNT_KEY:";

    /**
     * 设置密码验证码最多允许输错多少次，就必须换验证码
     * @author yeyi
     * @date 2019/5/10 15:58
     **/
    public static final int PWD_SMS_ERR_COUNT_MAX = 5;

    /**
     * 记录验证码错误的超时时间：一天
     * @author yeyi
     * @date 2019/5/10 15:38
     **/
    public static final int PWD_SMS_CODE_TIME_OUT = 24*60;
    
    public static final String sendSMSKey = "gmall:gmall-thirdparty-soa:SMSService:sendSMS:";

//    @Resource(name = "redisService")
//    private RedisService redisService;
    
    /**
     * 存储字典
     */
    private static HashMap<String, String> wordMap = null;
    

    /**
     * 校验密码通过
     */
    public static final String CHECK_PWD_SUCCESS = "ok";
    
    /**
     * 某验证码输错次数 +1
     * @author: yeyi
     * @date: 2019/5/10 15:20
     * @param mobile 手机号
     * @param smsCode 正确的验证码
     * @return 输错次数是否已超出限制
     **/
//    public boolean addSmsCodeErrCount(String mobile, String smsCode){
//        logger.info("addSmsCodeErrCoun in: {},{}", mobile, smsCode);
//        String key = this.makeKey(mobile, smsCode);
//        Object old = redisService.get(key);
//        if(null==old){
//            redisService.put(key,"1", PWD_SMS_CODE_TIME_OUT);
//            return false;
//        }
//
//        Integer count = Integer.parseInt(old.toString());
//        count ++;
//        redisService.put(key, count.toString(), PWD_SMS_CODE_TIME_OUT);
//        logger.info("addSmsCodeErrCoun out: {}", count);
//        return count >= PWD_SMS_ERR_COUNT_MAX;
//    }
//
//    /**
//     * 清除错误次数
//     * @author yeyi
//     * @date 2019/5/10 16:45
//     **/
//    public void clearSmsCodeErrCount(String mobile, String smsCode){
//        redisService.remove(this.makeKey(mobile, smsCode));
//    }

    /**
     * 记录错误次数的 key 内部用
     * @author yeyi
     * @date 2019/5/10 16:48
     **/
    private String makeKey(String mobile, String smsCode){
        StringBuilder sb = new StringBuilder(PWD_SMS_ERR_COUNT_KEY);
        sb.append(":");
        sb.append(mobile);
        sb.append(":");
        sb.append(smsCode);
        return sb.toString();
    }

    /**
     * 返回保存设置密码验证码的 redis key
     * @author yeyi
     * @date 2019/5/10 16:06
     **/
    public String makeSmsCodeKey(String mobile){
        StringBuilder sb = new StringBuilder(sendSMSKey);
        sb.append(mobile);
        sb.append("_SMS");
        return sb.toString();
    }

    /**
     * 清除 redis 中手机设置密码的验证码，使该验证码不能再使用
     * @author yeyi
     * @date 2019/5/10 16:30
     **/
//    public void clearRedisSmsCode(String mobile){
//        redisService.remove(this.makeSmsCodeKey(mobile));
//    }
    
    /**
     * 校验密码是否符合安全标准，不符合返回原因，符合返回 CHECK_PWD_SUCCESS
     * @param pwd
     * @author: yeyi
     * @date: 2019年5月17日
     */
    public String checkPwd(String password, String account) {
    	final Base64.Decoder decoder = Base64.getDecoder();
        String pwd = null;
		try {
			pwd = new String(decoder.decode(password), "gb2312");
		} catch (UnsupportedEncodingException e) {
			logger.error("decode err: ", e);
			return "解码异常";
		}

        if(StringUtils.isBlank(pwd) || pwd.length()<8){
            return "密码字符数不能小于8";
        }
    	
    	int typeNum = 0;
    	// 判断密码是否包含数字
    	if( pwd.matches(".*\\d+.*") ) {
    		typeNum++;
    	}
    	
    	// 判断密码是否包含小写字母
    	if( pwd.matches(".*[a-z]+.*") ) {
    		typeNum++;
    	}
    	
    	// 判断密码是否包含大写字母
    	if( pwd.matches(".*[A-Z]+.*") ) {
    		typeNum++;
    	}
    	
    	// 判断密码是否包含特殊符号
    	if( pwd.matches(".*[~!@#$%^&*()_+|<>,.?/:;'\\[\\]{}\"]+.*") ) {
    		typeNum++;
    	}
    	
    	if(typeNum<3) {
    		return "大写字母、小写字母、数字、特殊符号这四类字符中必须出现至少三类";
    	}

    	if(-1!=account.indexOf(pwd) || -1!=pwd.indexOf(account)){
    	    return "密码不能与账号太接近";
        }

    	if(this.isWords(pwd)) {
    		return "密码不能使用常见英文单词";
    	}
    	
    	return CHECK_PWD_SUCCESS;
    }
    
    /**
     * 判断 str是否是一个英文单词
     * @author yeyi
     * @date 2019/5/20 14:43
     **/
    public boolean isWords(String str){
    	if(StringUtils.isBlank(str)) {
    		return false;
    	}
    	
    	HashMap<String, String> words = this.getWordMap();
		if(null==words) {
			logger.error("wordMa empty");
    		return false; 
    	}
    	
    	return null!=wordMap.get(str.toLowerCase());
    }
    
    /**
     * 获取字典对象，如果是第一次拿，要从文件中读
     * @return
     * @author: yeyi
     * @date: 2019年5月20日
     */
    private synchronized HashMap<String, String> getWordMap(){
		if(null!=wordMap) {
			return wordMap;
		}
		
		// 从文件中读
		BufferedReader in = null;
    	try {
            InputStream path = this.getClass().getResourceAsStream("/words.txt");
            in = new BufferedReader(new InputStreamReader(path));
            String line;
            wordMap = new HashMap<String, String>(2096);
            int i=0;
            while ((line = in.readLine()) != null) {
            	if(StringUtils.isNotBlank(line)) {
            		// 只用 key 不用 value
            		wordMap.put(line, "");
            		i++;
            	}
            }
            
            logger.info("getWordSe words num: {}", i);
		} catch (Exception e) {
			logger.error("isWord err: ", e);
			wordMap = null;
			return null;
		} finally {
			if(null!=in) {
				try {
					in.close();
				} catch (IOException e2) {
					e2.printStackTrace();
					logger.error("isWord close err: ", e2);
				}
			}
		}
    	
    	return wordMap;
    }
}
